<template>
    <div class="flex flex-col h-full tts-container">
        <n-card class="flex flex-col grow" :bordered="false">
            <div class="text-container flex grow">
                <n-input v-model:value="text" type="textarea" placeholder="需要转换的文本" />
            </div>
            <n-select class="my-3" v-model:value="voice" :options="options" placeholder="选择音色" />
            <!-- 预览 start -->
            <n-space vertical v-if="configStore.review" class="audio-review shadow-md p-3 rounded">
                <n-slider class="audio-slider" :step="1" :max="maxStep" v-model:value="stepValue" :tooltip="false" />
                <n-space justify="space-between">
                    <n-button v-if="!isPlay" text style="font-size: 28px" :disabled="canPlay" @click="play">
                        <n-icon>
                            <PlayCircleOutline />
                        </n-icon>
                    </n-button>
                    <n-button v-else text style="font-size: 28px" @click="pause">
                        <n-icon>
                            <PauseCircleOutline />
                        </n-icon>
                    </n-button>
                    <n-button text @click="downloadAudio">
                        下载
                    </n-button>
                </n-space>
            </n-space>
        </n-card>
        <n-card class="convert h-16" :bordered="false">
            <!-- 预览 end -->
            <n-button style="width: 8rem;" type="info" :loading="loading" @click="ttsStart">转换</n-button>
        </n-card>
        <audio id="audio" :src="audioSrc"></audio>
    </div>
</template>

<script lang="ts" setup>
import { useConfigStore } from '@/stores/config';
import { ref } from 'vue';
import { dialog, path } from '@tauri-apps/api';
import { writeBinaryFile } from '@tauri-apps/api/fs';
import { useMessage } from 'naive-ui';
import { PlayCircleOutline, PauseCircleOutline } from '@vicons/ionicons5';

const message = useMessage();
const configStore = useConfigStore();

const text = ref();
const voice = ref(null);
const loading = ref(false);
const canPlay = ref(true);
const audioSrc = ref('');

const maxStep = ref(120);
const stepValue = ref(0);
const isPlay = ref(false);
const file = ref<Blob>();

const options = [
    {
        label: 'alloy',
        value: 'alloy'
    },
    {
        label: 'echo',
        value: 'echo'
    },
    {
        label: 'fable',
        value: 'fable'
    },
    {
        label: 'onyx',
        value: 'onyx'
    },
    {
        label: 'nova',
        value: 'nova'
    },
    {
        label: 'shimmer',
        value: 'shimmer'
    }
]


/**
 * 保存文件
 * @param file 
 */
const downloadFile = async (file?: Promise<ArrayBuffer>) => {
    const basePath = await path.downloadDir();
    let selPath = await dialog.save({
        title: '保存文件: ',
        defaultPath: basePath,
        filters: [{
            name: '*',
            extensions: ['mp3']
        }]
    });
    if (undefined === selPath || null === selPath) {
        alert('取消保存');
        return;
    }
    file?.then((buffer) => {
        writeBinaryFile({
            contents: new Uint8Array(buffer),
            path: selPath!
        }).then(() => alert('文件保存成功: ' + selPath))
    })
}
/**
 * 播放
 */
const playReview = (blob: Blob) => {
    const url = window.URL.createObjectURL(blob);
    audioSrc.value = url;
    canPlay.value = false;
    const audio = document.querySelector('#audio')! as HTMLAudioElement;
    audio.addEventListener('loadedmetadata', function () {
        maxStep.value = audio.duration;
    });
}
/**
 * 开始播放
 */
const play = () => {
    const audio = document.getElementById('audio')! as HTMLAudioElement;
    audio.play();
    isPlay.value = true;
    audio.addEventListener('timeupdate', function () {
        stepValue.value = audio.currentTime;
    });
    audio.onended = () => {
        isPlay.value = false;
        stepValue.value = 0;
    }
}
/**
 * 暂停
 */
const pause = () => {
    const audio = document.getElementById('audio')! as HTMLAudioElement;
    audio.pause()
    isPlay.value = false;
}
/**
 * 下载文件
 */
const downloadAudio = async () => {
    await downloadFile(file.value?.arrayBuffer());
}

/**
 * 开始转换
 */
const ttsStart = () => {
    if ('' == text.value || null === text.value) {
        return;
    }
    if (!voice.value) {
        message.warning('开始转换前请先选择音色!!!');
        return;
    }
    loading.value = true;
    if (undefined !== text.value && undefined !== text.value) {
        fetch("https://api.openai.com/v1/audio/speech", {
            method: "POST",
            headers: {
                Authorization: "Bearer " + configStore.apikey,
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                model: "tts-1-hd",
                input: text.value,
                voice: voice.value,
            }),
        })
            .then(response => {
                if (!response.ok) {
                    if (response.status === 401) {
                        throw new Error('请配置正确的 openai key')
                    }
                    if (response.status === 429) {
                        throw new Error('你的请求过快，请稍后再试')
                    }
                    else {
                        throw new Error(`HTTP error! status: ${response.status}`);
                    }
                }
                return response.blob()
            })
            .then((blob) => {
                if (configStore.review) {
                    message.success('转换成功，预览播放或者是手动下载');
                    playReview(blob);
                    file.value = blob;
                    loading.value = false;
                } else {
                    downloadFile(blob.arrayBuffer())
                    loading.value = false;
                }
            })
            .catch((error) => {
                message.error(error.message);
                // 记得关闭loading
                loading.value = false;
            })
    }
}

</script>

<style lang="scss">
.tts-container {

    .n-card__content {
        display: flex;
        flex-direction: column;

        .n-input.n-input--textarea {
            display: flex;
            flex-grow: 1;
        }
    }

    .convert {
        .n-card__content {
            display: flex;
            flex-wrap: wrap;
            align-content: center;
        }
    }

    .audio-slider {
        margin-bottom: 0.6rem;
    }
}
</style>